[AWS IoT]クライアントからのメッセージをS3へ保存する環境をRakeで構築する
はじめに
AWS IoTをぼちぼちと触り始めた、t-hondaです。手始めにクライアントからのメッセージをS3に保存する設定を行ってみたのですが、その流れをソースにしておきたいと思い、Rakeで設定を書いてみました。ソースにすることで
- 処理の流れが分かり易くなる
- 再利用がしやすくなる
などのメリットがあるかと思います。
今回行ったこと
先にも書いたように、クライアントからのメッセージを送信し、AWS IoTで受信してS3に保存するようにしました。クライアントとしてはMQTTプロトコルで送信できるruby-mqttを使用しました。
AWS IoTの設定をRakeで書いたのですが、処理の流れは以下のようになります。
- Thingを作成する
- 証明書を作成する
- Policyを作成する
- 証明書をPolicyとThingに紐づける
- IAMロールを作成する
- Ruleを作成する
この処理の流れに沿って、Rakeを記述しました。
作成したRake
では、Rakeについてです。Rakefile本体と、Gemfileやヘルパーなども作成しました。それらについて書いていきます。尚、実行環境はRuby 2.2です。
1.Gemfile
Rakeと、AWS IoTの設定にAWS SDK for Ruby V2を使用するので、それらを記述してbundle installしました。
Gemfile
# A sample Gemfile source "https://rubygems.org" # gem "rails" gem "rake" gem "aws-sdk"
2.ヘルパー
AWS SDKのオブジェクトの作成は、証明書ファイルの記述処理などは、ヘルパーファイルに外出ししました。
rake_helper
require "aws-sdk" def iot @iot ||= Aws::IoT::Client.new(region: @region) end def iam @iam ||= Aws::IAM::Client.new(region: @region) end def write_cert_file(file_path, value) File.open(file_path, "w") do |f| f.print value end end
AWS IoT、IAMを操作するためのクライアントオブジェクトを返却するメソッドを定義しています。また証明書ファイルをローカルに出力するための「write_cert_file」も定義しています。
3.Rakefile
ではRakefileです。
Rakefile
require_relative "rake_helper" THING_NAME = "t-honda-thing" POLICY_NAME = "t-honda-dsl-policy" ROLE_NAME = "t_honda_dsl_role" ROLE_POLICY_NAME = "t-honda-dsl-role-policy" RULE_NAME = "t_honda_dsl_rule" S3_BACKET_NAME = "t-honda-iot" @region = "ap-northeast-1" task :default => [ :create_thing, :create_certificate_keys, :create_policy, :attach_certificate, :create_role, :create_rule, ] task :create_thing do iot.create_thing({thing_name: THING_NAME}) end task :create_certificate_keys do resp = iot.create_keys_and_certificate({set_as_active: true}) write_cert_file("./pems/thing-public-key.pem", resp.key_pair.public_key) write_cert_file("./pems/private-key.pem", resp.key_pair.private_key) write_cert_file("./pems/cert.pem", resp.certificate_pem) @created_certificate_arn = iot. describe_certificate({certificate_id: resp.certificate_id}). certificate_description. certificate_arn end task :create_policy do File.open("./policy.json", "r") do |f| iot.create_policy({policy_name: POLICY_NAME, policy_document: f.read }) end end task :attach_certificate do iot.attach_principal_policy({policy_name: POLICY_NAME, principal: @created_certificate_arn }) iot.attach_thing_principal({thing_name: THING_NAME, principal: @created_certificate_arn, }) end task :create_role do File.open("./assume_role_policy.json", "r") do |f| iam.create_role({role_name: ROLE_NAME, assume_role_policy_document: f.read }) end File.open("./role_policy.json", "r") do |f| iam.put_role_policy({role_name: ROLE_NAME, policy_name: ROLE_POLICY_NAME, policy_document: f.read, }) end @created_role_arn = iam. get_role({role_name: ROLE_NAME}). data. role. arn end task :create_rule do iot.create_topic_rule({rule_name: RULE_NAME, topic_rule_payload: {sql: "SELECT * FROM 'topic/test'", actions: [ { s3: { role_arn: @created_role_arn, bucket_name: S3_BACKET_NAME, key: "ruby-dsl", } } ], rule_disabled: false } }) end
必要な処理を「タスク」に分割し、各タスクを「task ・・・ do」のブロック内に記述しています。「task :create_thing do」以下のタスクは、それぞれが先に「今回行ったこと」で書いた各処理に当たります。一番上の「task :default => 」はRakeのデフォルトタスクを定義しており、デフォルトで「task :create_thing do」以降の全タスクを順次実行することを定義しています。
以下、各タスクについての簡単な説明です。
3-1.create_thing
Thingの作成を行っています。これは単純にAWS SDKの「create_thing」メソッドを呼び出しているだけです。
3-2.create_certificate_keys
「create_keys_and_certificate」メソッドを呼び出して証明書を作成しています。戻り値より作成した3種類の証明書を取得し、ローカルに出力しています。また後に使用するため、証明書のcertificate_arnを変数に保存しています。
3-3.create_policy
Policyを作成しています。Policyの設定をjsonで渡していますが、そのjsonは以下のようになります。
policy.json
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action":["iot:*"], "Resource": ["*"] }] }
3-4.attach_certificate
証明書をPolicyとThingに紐づけています。「attach_principal_policy」「attach_thing_principal」メソッドを呼び出していますが、このとき3-2.で作成した証明書のcertificate_arnを渡しています。
3-5.create_role
AWS IoTがS3に保存するためのIAMロールを作成しています。信頼関係、ロールポリシーをjsonで渡していますが、それぞれのjsonは以下のようになります。
assume_role_policy.json
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
role_policy.json
{ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::t-honda-iot/*" } }
3-6.create_rule
AWS IoTからS3に出力するためのルールを作成しています。S3のロールには、3-4.で作成したロールのarnを渡しています。
作成したAWS IoTの設定
上記のRakefileを実行すると以下の様なAWS IoTの設定が作成されます。
クライアントからのメッセージ送信
最後にクライアントからメッセージを送信してみます。先に書いたようにruby-mqttを使用し、以下の様なソースでメッセージを送信してみました。
require "mqtt" MQTT::Client.connect(host: "XXXXXXX.iot.ap-northeast-1.amazonaws.com", port: 8883, ssl: true, cert_file: "./pems/cert.pem", key_file: "./pems/private-key.pem", ca_file: "rootCA.pem") do |client| client.publish("topic/test", "publish test") end
「host〜」の箇所には、AWS IoTのendpointの値を記述します。マネージメントコンソールよりThingを選択し「REST API endpoint」の値を参照してください。
「cert_file」「key_file」はRakeにて作成した証明書ファイルです。「ca_file」は以下のコマンドでダウンロードしました。
$ wget https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem -O rootCA.pem
このRubyスクリプトを実行すると、S3のバケットにメッセージを出力したファイルが作成されると思います。
まとめ
AWS IoTの設定については作成するモノが多いのですが、流れを把握すれば分かり易いように感じました。また設定をソースにすることで、例えば証明書のcertificate_arnをPolicy・Thingに渡すところなどの流れが分かり易くなったと感じました。
参考サイト
AWS IoTとRuby製MQTTクライアントでPub/Subしてみた
OpenBlocks BX1とAWS IoTをつなげてAmazon S3にメッセージをなげてみる